home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Communications / pcomm / Source / x_rcv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  11.8 KB  |  527 lines

  1. /*
  2.  * Receive a list of files using a version of Ward Christensen's file
  3.  * transfer protocol.  A non-zero return code means the user must acknowledge
  4.  * an error condition (a user generated abort returns a 0).  Write errors
  5.  * are considered fatal.
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <curses.h>
  10. #include "config.h"
  11. #include "dial_dir.h"
  12. #include "misc.h"
  13. #include "xmodem.h"
  14.  
  15. unsigned char buf[1029];
  16. char file_name[15];
  17. long file_length;
  18.  
  19. static int err_method, tot_err, block_size;
  20.  
  21. int
  22. rcv_xmodem(win, list, type, fast)
  23. WINDOW *win;
  24. char *list;
  25. int type, fast;
  26. {
  27.     extern char *protocol[];
  28.     FILE *fp, *my_fopen();
  29.     int i, default_err, is_batch, max_block, code, file_count, got_hdr;
  30.     int hours, mins, secs, len;
  31.     static int send_first();
  32.     long block, recv, partial;
  33.     float percent, performance;
  34.     unsigned char blk;
  35.     unsigned int sleep();
  36.     char *file, *name, *strcpy(), *strrchr(), *strtok();
  37.     void cancel_xfer();
  38.                     /* which protocol? */
  39.     switch (type) {
  40.         case XMODEM:
  41.             default_err = CRC_CHECKSUM;
  42.             is_batch = 0;
  43.             max_block = 128;
  44.             break;
  45.         case XMODEM_1k:
  46.             default_err = CRC_CHECKSUM;
  47.             is_batch = 0;
  48.             max_block = 1024;
  49.             break;
  50.         case MODEM7:
  51.             default_err = CHECKSUM;
  52.             is_batch = 1;
  53.             max_block = 128;
  54.             break;
  55.         case YMODEM:
  56.             default_err = CRC;
  57.             is_batch = 1;
  58.             max_block = 1024;
  59.             performance = 1.09;
  60.             break;
  61.         case YMODEM_G:
  62.             default_err = NONE;
  63.             is_batch = 1;
  64.             max_block = 1024;
  65.             performance = 1.02;
  66.             break;
  67.         default:
  68.             return(1);
  69.     }
  70.  
  71.     tot_err = 0;
  72.     file_count = 0;
  73.     mvwaddstr(win, 2, 24, protocol[type]);
  74.     mvwaddstr(win, 11, 24, "0  ");
  75.  
  76.     while (1) {
  77.         file_count++;
  78.         file_length = 0L;
  79.                     /* user supplied name */
  80.         if (!is_batch) {
  81.             if (file_count > 1)
  82.                 break;
  83.  
  84.             file = strtok(list, " \t");
  85.                     /* dissect the file name */
  86.             if ((name = strrchr(file, '/')))
  87.                 strcpy(file_name, ++name);
  88.             else
  89.                 strcpy(file_name, file);
  90.         }
  91.                     /* get the modem7 file name */
  92.         if (type == MODEM7) {
  93.             if (code = rcv_modem7(win, default_err))
  94.                 return(code +1);
  95.  
  96.             file = file_name;
  97.         }
  98.                     /* get the block 0 */
  99.         if (type == YMODEM || type == YMODEM_G) {
  100.             if (code = send_first(win, max_block, default_err))
  101.                 return(code +1);
  102.  
  103.             if (code = rcv_ymodem(win))
  104.                 return(code +1);
  105.  
  106.                     /* at the end? */
  107.             if (buf[3] == '\0') {
  108.                 beep();
  109.                 wrefresh(win);
  110.                 putc_line(ACK);
  111.                 sleep(1);
  112.                 return(0);
  113.             }
  114.             file = file_name;
  115.         }
  116.                     /* any trouble? */
  117.         if (file_name[0] == '\0')
  118.             continue;
  119.  
  120.         clear_line(win, 3, 24, TRUE);
  121.         waddstr(win, file_name);
  122.                     /* if file length is known */
  123.         if (file_length != 0L) {
  124.             mvwprintw(win, 4, 24, "%-10ld", file_length);
  125.  
  126.             secs = (file_length * 10.0 / dir->baud[dir->d_cur]) * performance;
  127.             hours = secs / 3600;
  128.             mins = (secs % 3600) / 60;
  129.             secs = (secs % 3600) % 60;
  130.  
  131.             mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
  132.         }
  133.                     /* some starting numbers */
  134.         mvwaddstr(win, 7, 24, "0    ");
  135.         if (file_length != 0L && fast)
  136.             mvwaddstr(win, 8, 24, "0%  ");
  137.         if (fast)
  138.             mvwaddstr(win, 9, 24, "0          ");
  139.         mvwaddstr(win, 10, 24, "0 ");
  140.         clear_line(win, 12, 24, TRUE);
  141.         waddstr(win, "NONE");
  142.         wrefresh(win);
  143.  
  144.         /*
  145.          * If the user supplied the name, write permission is checked
  146.          * by the get_names() routine in xfer_menu().  If modem7
  147.          * or ymodem supplied name, the name is unique and the write
  148.          * permission on the directory is checked by the change_name()
  149.          * routines.  However, this is required for systems with
  150.          * SETUID_BROKE set.
  151.          */
  152.                     /* open the file */
  153.         if (!(fp = my_fopen(file, "w"))) {
  154.             beep();
  155.             clear_line(win, 12, 24, TRUE);
  156.             wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
  157.             wrefresh(win);
  158.             cancel_xfer(DOWN_LOAD);
  159.             return(1);
  160.         }
  161.                     /* ACK the block 0 */
  162.         if (type == YMODEM || type == YMODEM_G)
  163.             putc_line(ACK);
  164.  
  165.         if (code = send_first(win, max_block, default_err)) {
  166.             fclose(fp);
  167.             return(code +1);
  168.         }
  169.                     /* here we go... */
  170.         clear_line(win, 12, 24, TRUE);
  171.         waddstr(win, "NONE");
  172.         wrefresh(win);
  173.         blk = 1;
  174.         block = 1L;
  175.         recv = 0L;
  176.         got_hdr = 1;
  177.         while (1) {
  178.             code = rcv_block(win, got_hdr, max_block, blk);
  179.  
  180.             if (code < 0) {
  181.                 fclose(fp);
  182.                 return(code +1);
  183.             }
  184.             got_hdr = 0;
  185.                     /* are we done? */
  186.             if (buf[0] == EOT) {
  187.                 if (!is_batch) {
  188.                     beep();
  189.                     wrefresh(win);
  190.                     sleep(1);
  191.                 }
  192.                 break;
  193.             }
  194.                     /* if not a duplicate block */
  195.             if (!code) {
  196.                 if (file_length != 0L) {
  197.                     partial = file_length - recv;
  198.                     if (partial > (long) block_size)
  199.                         len = block_size;
  200.                     else
  201.                         len = partial;
  202.                 }
  203.                 else
  204.                     len = block_size;
  205.  
  206.                 if (fwrite((char *) &buf[3], sizeof(char), len, fp) != len) {
  207.                     beep();
  208.                     clear_line(win, 12, 24, TRUE);
  209.                     wattrstr(win, A_BOLD, "WRITE ERROR");
  210.                     wrefresh(win);
  211.                     cancel_xfer(DOWN_LOAD);
  212.                     fclose(fp);
  213.                     /* fatal */
  214.                     return(1);
  215.                 }
  216.                 mvwprintw(win, 7, 24, "%-5ld", block);
  217.                 recv = recv + (unsigned int) len;
  218.                 if (fast)
  219.                     mvwprintw(win, 9, 24, "%-10ld", recv);
  220.                 blk++;
  221.                 block++;
  222.             }
  223.             /*
  224.              * If the length is known, give the same status
  225.              * report as uploading
  226.              */
  227.             if (file_length != 0L && fast) {
  228.                 percent = recv * 100.0 / file_length;
  229.                 if (percent > 100.0)
  230.                     percent = 100.0;
  231.                 mvwprintw(win, 8, 24, "%0.1f%%", percent);
  232.             }
  233.             wrefresh(win);
  234.             putc_line(ACK);
  235.         }
  236.         if (file_length != 0L && fast) {
  237.             mvwaddstr(win, 8, 24, "100%  ");
  238.             wrefresh(win);
  239.         }
  240.         /*
  241.          * If the file length is not known, search backwards from
  242.          * the end of the file until you find a character that is
  243.          * not the ^Z padding character.
  244.          */
  245.         if (file_length == 0L) {
  246.             for (i=block_size+2; i>2; i--) {
  247.                 if (buf[i] != CTRLZ)
  248.                     break;
  249.             }
  250.             file_length = recv - (unsigned int) block_size + (unsigned int) i -2L;
  251.             fclose(fp);
  252.             if (fix_length(file, file_length)) {
  253.                 beep();
  254.                 clear_line(win, 12, 24, TRUE);
  255.                 wattrstr(win, A_BOLD, "TRUNCATE ERROR");
  256.                 wrefresh(win);
  257.                 sleep(1);
  258.             }
  259.         }
  260.         else
  261.             fclose(fp);
  262.                     /* ACK the EOT */
  263.         putc_line(ACK);
  264.     }
  265.     return(0);
  266. }
  267.  
  268. /*
  269.  * Send the first character to start the transmission and set the error
  270.  * checking method.  Returns the standard error codes or 0 on success.
  271.  * The variables err_method and block_size are global.
  272.  */
  273.  
  274. static int
  275. send_first(win, max_block, default_err)
  276. WINDOW *win;
  277. int max_block, default_err;
  278. {
  279.     int i, err_count;
  280.     unsigned int sleep();
  281.     void cancel_xfer();
  282.                     /* default error method */
  283.     err_method = default_err;
  284.     if (default_err == CRC_CHECKSUM)
  285.         err_method = CRC;
  286.                     /* send the first char */
  287.     err_count = 0;
  288.     while (err_count < MAX_ERRORS*2) {
  289.         mvwprintw(win, 10, 24, "%-2d", err_count);
  290.  
  291.                     /* check for keyboard abort */
  292.         if (wgetch(win) == ESC) {
  293.             beep();
  294.             clear_line(win, 12, 24, TRUE);
  295.             waddstr(win, "ABORTED");
  296.             wrefresh(win);
  297.             cancel_xfer(DOWN_LOAD);
  298.             sleep(3);
  299.             return(ABORT);
  300.         }
  301.                     /* switch to checksum? */
  302.         if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
  303.             err_method = CHECKSUM;
  304.  
  305.                     /* send error method code */
  306.         clear_line(win, 5, 24, TRUE);
  307.         switch (err_method) {
  308.             case CHECKSUM:
  309.                 waddstr(win, "CHECKSUM");
  310.                 putc_line(NAK);
  311.                 break;
  312.             case CRC:
  313.                 waddstr(win, "CRC");
  314.                 putc_line('C');
  315.                 break;
  316.             case NONE:
  317.                 waddstr(win, "NONE");
  318.                 putc_line('G');
  319.                 break;
  320.         }
  321.         /*
  322.          * We've cut the delay time in half, so we double
  323.          * the allowable errors
  324.          */
  325.         if ((i = getc_line(5)) == -1) {
  326.             err_count++;
  327.             clear_line(win, 12, 24, TRUE);
  328.             waddstr(win, "NO RESPONSE");
  329.             wrefresh(win);
  330.             continue;
  331.         }
  332.         buf[0] = i;
  333. #ifdef DEBUG
  334.         fprintf(stderr, "send_first: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
  335. #endif /* DEBUG */
  336.  
  337.         switch (buf[0]) {
  338.             case SOH:    /* small block follows */
  339.                 block_size = 128;
  340.                 return(0);
  341.             case STX:    /* large block follows */
  342.                 if (max_block == 1024) {
  343.                     block_size = 1024;
  344.                     return(0);
  345.                 }
  346.                 /* fall thru */
  347.             default:
  348.                 err_count++;
  349.                 clear_line(win, 12, 24, TRUE);
  350.                 waddstr(win, "BAD HEADER");
  351.                 wrefresh(win);
  352.                     /* read some garbage... */
  353.                 while(fread_line(buf, 1028, 1) != -1)
  354.                     ;
  355.                 putc_line(NAK);
  356.                 break;
  357.         }
  358.     }
  359.     beep();
  360.     clear_line(win, 12, 24, TRUE);
  361.     wattrstr(win, A_BOLD, "TIMED OUT");
  362.     wrefresh(win);
  363.     return(ERROR);
  364. }
  365.  
  366. /*
  367.  * Receive a block of info from the host.  Returns a 0 on success, a 1 on
  368.  * a duplicate block or the standard error codes.  The variables
  369.  * err_method and block_size are global.
  370.  */
  371.  
  372. int
  373. rcv_block(win, got_hdr, max_block, blk)
  374. WINDOW *win;
  375. int got_hdr, max_block;
  376. unsigned char blk;
  377. {
  378.     int i, err_count, bad_block, out_of_sync;
  379.     unsigned short crc, calc_crc();
  380.     unsigned int packet, sleep();
  381.     unsigned char blk_compliment, calc_sum(), crc_1, crc_2;
  382.     void cancel_xfer();
  383.  
  384.     err_count = 0;
  385.     while (err_count < MAX_ERRORS) {
  386.         mvwprintw(win, 10, 24, "%-2d", err_count);
  387.         mvwprintw(win, 11, 24, "%-3d", tot_err);
  388.  
  389.                     /* scan the keyboard for abort */
  390.         if (wgetch(win) == ESC) {
  391.             beep();
  392.             clear_line(win, 12, 24, TRUE);
  393.             waddstr(win, "ABORTED");
  394.             wrefresh(win);
  395.             cancel_xfer(DOWN_LOAD);
  396.             sleep(3);
  397.             return(ABORT);
  398.         }
  399.                     /* have we already got a hdr? */
  400.         if (!got_hdr) {
  401.             if ((i = getc_line(10)) == -1) {
  402.                 err_count++;
  403.                 tot_err++;
  404.                 clear_line(win, 12, 24, TRUE);
  405.                 waddstr(win, "NO RESPONSE");
  406.                 wrefresh(win);
  407.                 continue;
  408.             }
  409.             buf[0] = i;
  410. #ifdef DEBUG
  411.             fprintf(stderr, "rcv_block: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
  412. #endif /* DEBUG */
  413.                     /* what'd we get? */
  414.             switch (buf[0]) {
  415.                 case EOT:    /* we're done! */
  416.                     clear_line(win, 12, 24, TRUE);
  417.                     waddstr(win, "TRANSFER COMPLETE");
  418.                     wrefresh(win);
  419.                     sleep(1);
  420.                     return(0);
  421.                 case SOH:    /* small block follows */
  422.                     block_size = 128;
  423.                     break;
  424.                 case STX:    /* large block follows */
  425.                     if (max_block == 1024) {
  426.                         block_size = 1024;
  427.                         break;
  428.                     }
  429.                     /* fall thru... */
  430.                 default:
  431.                     err_count++;
  432.                     tot_err++;
  433.                     clear_line(win, 12, 24, TRUE);
  434.                     waddstr(win, "BAD HEADER");
  435.                     wrefresh(win);
  436.  
  437.                     /* read some garbage... */
  438.                     while(fread_line(buf, 1028, 1) != -1)
  439.                         ;
  440.                     putc_line(NAK);
  441.                     continue;
  442.             }
  443.         }
  444.                     /* read the rest of the packet */
  445.         packet = block_size + 2 + (err_method == CHECKSUM ? 1 : 2);
  446.         if (fread_line(&buf[1], packet, 10) == -1) {
  447.             clear_line(win, 12, 24, TRUE);
  448.             waddstr(win, "TIMED OUT");
  449.             wrefresh(win);
  450.             putc_line(NAK);
  451.             err_count++;
  452.             tot_err++;
  453.             continue;
  454.         }
  455.  
  456.         /*
  457.          * Validation of the packet includes checking the
  458.          * block number, its complement, and the crc/checksum.
  459.          */
  460.         out_of_sync = 0;
  461.         blk_compliment = ~blk;
  462.         if (buf[1] != blk || buf[2] != blk_compliment)
  463.             out_of_sync++;
  464.  
  465.         bad_block = 0;
  466.         switch (err_method) {
  467.             case CHECKSUM:
  468. #ifdef DEBUG
  469.                 fprintf(stderr, "blk=%d, checksum=%d\n", blk, calc_sum(&buf[3], block_size));
  470. #endif /* DEBUG */
  471.                 if (buf[block_size +3] != calc_sum(&buf[3], block_size))
  472.                     bad_block++;
  473.                 break;
  474.             case CRC:
  475.                 crc = calc_crc(&buf[3], block_size);
  476.                 crc_1 = crc >> 8;
  477.                 crc_2 = crc;
  478. #ifdef DEBUG
  479.                 fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, crc_1, crc_2);
  480. #endif /* DEBUG */
  481.                 if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
  482.                     bad_block++;
  483.                 break;
  484.             case NONE:
  485.                 return(0);
  486.         }
  487.                     /* handle errors */
  488.         if (bad_block) {
  489.             clear_line(win, 12, 24, TRUE);
  490.             if (err_method == CRC)
  491.                 waddstr(win, "CRC FAILED");
  492.             else
  493.                 waddstr(win, "CHECKSUM FAILED");
  494.             wrefresh(win);
  495.             putc_line(NAK);
  496.             err_count++;
  497.             tot_err++;
  498.             continue;
  499.         }
  500.                     /* not really an error */
  501.         if (out_of_sync) {
  502.             /*
  503.              * If a perfect packet is off by 1 block number,
  504.              * (a lost ACK could cause this) then treat it as
  505.              * a good block but don't write it to disk.
  506.              */
  507.             if (buf[1] == (unsigned char) blk-1)
  508.                 return(1);
  509.  
  510.             clear_line(win, 12, 24, TRUE);
  511.             waddstr(win, "OUT OF SYNC");
  512.             wrefresh(win);
  513.             putc_line(NAK);
  514.             err_count++;
  515.             tot_err++;
  516.             continue;
  517.         }
  518.         return(0);
  519.     }
  520.     beep();
  521.     clear_line(win, 12, 24, TRUE);
  522.     waddstr(win, "TOO MANY ERRORS");
  523.     wrefresh(win);
  524.     cancel_xfer(DOWN_LOAD);
  525.     return(ERROR);
  526. }
  527.